<template>
	<view>
		<button @click="initBlue()">1 初始化蓝牙</button>
		<view class="msg">{{msg1}}</view>

		<button @click="discovery()">2 搜索附近蓝牙设备</button>
		<scroll-view scroll-y class="box">
			<view class="item" v-for="(item,index) in blueDeviceList" @click="connect(item)" :key="index">
				<view>
					<text>设备id: {{ item.deviceId }}</text>
				</view>
				<view>
					<text>名称: {{ item.name }}</text>
				</view>
			</view>
		</scroll-view>
		<view class="msg">
			已选择设备的id--名称：<br>
			{{deviceId}}--{{deviceName}}
		</view>

		<button @click="getServices()">3 获取蓝牙服务</button>
		<scroll-view scroll-y class="box">
			<view class="item" v-for="(item,index) in servicesList" @click="showServiceId(item)" :key="index">
				<view>
					<text>服务id: {{ item.uuid }}</text>
				</view>
				<view>
					<text>是否为主服务: {{ item.isPrimary }}</text>
				</view>
			</view>
		</scroll-view>
		<view class="msg">
			已选择的服务id：<br>
			{{serviceId}}
		</view>

		<button @click="getCharacteristics()">4 获取特征值</button>
		<scroll-view scroll-y class="box">
			<view class="item" v-for="(item,index) in characteristicList" @click="showCharacteristic(item)" :key="index">
				<view>
					<text>特征值id: {{ item.uuid }}</text>
				</view>
				<view>
					<view>
						<text>支持操作:</text>
						<ul>
							<li>read：{{item.properties.read}}</li>
							<li>write：{{item.properties.write}}</li>
							<li>notify：{{item.properties.notify}}</li>
							<li>indicate：{{item.properties.indicate}}</li>
						</ul>
					</view>
				</view>
			</view>
		</scroll-view>
		<view class="msg">
			已选择的特征值id：<br>
			{{characteristicId}}
		</view>

		<button @click="notify()">5 开启消息监听</button>

		<button @click="send()">6 发送数据</button>
		<input class="input-mes" v-model="sendMes" />
		<view class="msg">
			发送结果：<br>
			{{msg6}}
		</view>

		<button @click="read()">7 读取数据</button>
		<view class="msg_x">
			<view class="msg_txt">
				监听到的内容：{{ message }}
			</view>
			<view class="msg_hex">
				监听到的内容（十六进制）：{{ messageHex }}
			</view>
		</view>

		<button @click="closeBlue()">8 断开连接</button>
		<view class="msg">{{msg8}}</view>
	</view>
</template>

<script setup>
	import {
		ref
	} from 'vue'

	// 搜索到的蓝牙设备列表
	const blueDeviceList = ref([])

	const msg1 = ref('')
	// 【1】初始化蓝牙
	function initBlue() {
		uni.openBluetoothAdapter({
			success(res) {
				msg1.value = '初始化蓝牙成功'
				console.log('初始化蓝牙成功')
				console.log(res)
			},
			fail(err) {
				msg1.value = '初始化蓝牙失败'
				console.log('初始化蓝牙失败')
				console.error(err)
			}
		})
	}

	// 【2】开始搜寻附近设备
	function discovery() {
		uni.startBluetoothDevicesDiscovery({
			success(res) {
				console.log('开始搜索')
				// 开启监听回调
				uni.onBluetoothDeviceFound(found)
			},
			fail(err) {
				console.log('搜索失败')
				console.error(err)
			}
		})
	}

	// 【3】找到新设备就触发该方法
	function found(res) {
		console.log(res)
		blueDeviceList.value.push(res.devices[0])
	}

	// 蓝牙设备的id和名称
	const deviceId = ref('')
	const deviceName = ref('')

	// 【4】连接设备
	function connect(data) {
		console.log(data)

		deviceId.value = data.deviceId
		deviceName.value = data.name

		uni.createBLEConnection({
			deviceId: deviceId.value,
			success(res) {
				console.log('连接成功')
				console.log(res)
				// 停止搜索
				stopDiscovery()
				uni.showToast({
					title: '连接成功'
				})
			},
			fail(err) {
				console.log('连接失败')
				console.error(err)
				uni.showToast({
					title: '连接成功',
					icon: 'error'
				})
			}
		})
	}

	// 【5】停止搜索
	function stopDiscovery() {
		uni.stopBluetoothDevicesDiscovery({
			success(res) {
				console.log('停止成功')
				console.log(res)
			},
			fail(err) {
				console.log('停止失败')
				console.error(err)
			}
		})
	}

	// 对应设备提供的服务列表
	const servicesList = ref([])
	// 【6】获取服务
	function getServices() {
		uni.getBLEDeviceServices({
			deviceId: deviceId.value,
			success(res) {
				console.log(res)
				servicesList.value = res.services
				uni.showToast({
					title: '获取服务成功'
				})
			},
			fail(err) {
				console.error(err)
				uni.showToast({
					title: '获取服务失败',
					icon: 'error'
				})
			}
		})
	}

	// 服务id
	const serviceId = ref('')
	// 展示所选的服务id
	function showServiceId(data) {
		serviceId.value = data.uuid
	}

	// 【7】获取特征值
	function getCharacteristics() {
		uni.getBLEDeviceCharacteristics({
			deviceId: deviceId.value,
			serviceId: serviceId.value,
			success(res) {
				characteristicList.value = res.characteristics
				console.log(res)
				uni.showToast({
					title: '获取特征值成功'
				})
			},
			fail(err) {
				console.error(err)
				uni.showToast({
					title: '获取特征值失败',
					icon: 'error'
				})
			}
		})
	}

	// 对应服务的特征值
	const characteristicList = ref([])
	// 所选特征值
	const characteristicId = ref('')
	// 展示所选的特征值
	function showCharacteristic(data) {
		characteristicId.value = data.uuid
	}

	// 【8】开启消息监听
	function notify() {
		uni.notifyBLECharacteristicValueChange({
			deviceId: deviceId.value,
			serviceId: serviceId.value,
			characteristicId: characteristicId.value,
			state: true,
			success(res) {
				console.log(res)
				listenValueChange()
				uni.showToast({
					title: '已开启监听'
				})
			},
			fail(err) {
				console.error(err)
				uni.showToast({
					title: '监听失败',
					icon: 'error'
				})
			}
		})
	}

	// ArrayBuffer转16进度字符串示例
	function ab2hex(buffer) {
		const hexArr = Array.prototype.map.call(
			new Uint8Array(buffer),
			function(bit) {
				return ('00' + bit.toString(16)).slice(-2)
			}
		)
		return hexArr.join('')
	}

	// 
	function hexCharCodeToStr(hexCharCodeStr) {
		var trimedStr = hexCharCodeStr.trim();
		var rawStr = trimedStr.substr(0, 2).toLowerCase() === "0x" ? trimedStr.substr(2) : trimedStr;
		var len = rawStr.length;
		if (len % 2 !== 0) {
			alert("存在非法字符!");
			return "";
		}
		var curCharCode;
		var resultStr = [];
		for (var i = 0; i < len; i = i + 2) {
			curCharCode = parseInt(rawStr.substr(i, 2), 16);
			resultStr.push(String.fromCharCode(curCharCode));
		}
		return resultStr.join("");
	}

	// 监听到的内容
	const message = ref('')
	const messageHex = ref('') // 十六进制

	// 【9】监听消息变化
	function listenValueChange() {
		uni.onBLECharacteristicValueChange(res => {
			console.log(res)
			let resHex = ab2hex(res.value)
			console.log(resHex)
			messageHex.value = resHex
			let result = hexCharCodeToStr(resHex)
			console.log(String(result))
			message.value = String(result)
		})
	}

	const msg6 = ref('')
	const sendMes = ref('')
	// 【10】发送数据
	function send() {
		// 向蓝牙设备发送一个0x00的16进制数据
		const buffer = new ArrayBuffer(sendMes.value.length)
		const dataView = new DataView(buffer)
		// dataView.setUint8(0, 0)

		for (var i = 0; i < sendMes.value.length; i++) {
			dataView.setUint8(i, sendMes.value.charAt(i).charCodeAt())
		}

		uni.writeBLECharacteristicValue({
			deviceId: deviceId.value,
			serviceId: serviceId.value,
			characteristicId: characteristicId.value,
			value: buffer,
			success(res) {
				msg6.value = '发送成功，内容为：' + sendMes.value
				console.log('writeBLECharacteristicValue success', res.errMsg)
				uni.showToast({
					title: 'write指令发送成功'
				})
			},
			fail(err) {
				msg6.value = '发送失败'
				console.error(err)
				uni.showToast({
					title: 'write指令发送失败',
					icon: 'error'
				})
			}
		})
	}

	// 【11】读取数据
	function read() {
		uni.readBLECharacteristicValue({
			deviceId: deviceId.value,
			serviceId: serviceId.value,
			characteristicId: characteristicId.value,
			success(res) {
				console.log(res)
				uni.showToast({
					title: 'read指令发送成功'
				})
			},
			fail(err) {
				console.error(err)
				uni.showToast({
					title: 'read指令发送失败',
					icon: 'error'
				})
			}
		})
	}

	const msg8 = ref('')
	// 断开蓝牙连接，释放资源
	function closeBlue() {
		uni.closeBluetoothAdapter({
			success(res) {
				console.log(res);
				msg8.value = '资源释放，蓝牙断开成功'
			}
		})
		uni.closeBLEConnection({
			deviceId: deviceId.value,
			success(res) {
				console.log(res);
				msg8.value = '资源释放，蓝牙断开成功'
			}
		})
	}
</script>

<style>
	.box {
		width: 720rpx;
		min-height: 100rpx;
		max-height: 400rpx;
		margin: 10rpx auto;
		border: 3px solid #cddcf8;
	}

	.item {
		padding: 10rpx;
		border-bottom: 2px solid #cddcf8;
	}

	button {
		width: 400rpx;
		margin: 10px 0 10px 10px;
		background-color: #e5ebf8;
		font-size: 14px;
	}

	.input-mes {
		border: 1px solid black;
		margin: 10px;
	}

	.msg {
		font-size: 12px;
		margin-left: 10px;
	}

	.msg_x {
		border: 3px solid #cddcf8;
		width: 98%;
		margin: 10rpx auto;
		box-sizing: border-box;
		padding: 20rpx;
	}

	.msg_x .msg_txt {
		margin-bottom: 20rpx;
	}
</style>